import { _decorator, Animation, AudioClip, clamp, Collider2D, color, Component, ERaycast2DType, ERigidBody2DType, EventTouch, instantiate, IPhysics2DContact, js, Node, ParticleSystem2D, PhysicsSystem2D, RaycastResult2D, Sprite, SpriteFrame, tween, Tween, UITransform, v2, v3, Vec2, Vec3 } from 'cc';
import { Person } from './Person';
import { MigaManager } from './MigaManager';
import { Subject, TweenTag } from './Subject';
import { EasingType } from '../../Scripts/Framework/Utils/TweenUtil';
import { GameData } from './GameData';
import { PolygonPlane } from './GameItems/PolygonPlane';
import { AudioManager } from '../../Scripts/Framework/Managers/AudioManager';
import { Box } from './GameItems/Box';
import { Basket } from './GameItems/Basket';
const { ccclass, property } = _decorator;

@ccclass('Item')
export class Item extends Subject {

    @property
    canUse = false;

    @property
    canTouchUse = false;

    @property
    canReverse = true;

    @property
    isBottle = false;

    @property
    canBounce = false;

    @property
    canHand = true;

    @property
    isInfusion = false;

    @property
    isLimitBottle = false;

    @property
    holdX = 0;

    @property
    holdY = 0;

    @property
    layAngle = 0;

    @property
    touchAngle = 0;

    @property
    handAngle = 0;

    @property(AudioClip)
    clip: AudioClip;

    @property(Sprite)
    water: Sprite;

    @property(Node)
    spawn: Node;

    @property([SpriteFrame])
    changeSfs: SpriteFrame[] = [];

    @property
    size = 0;

    sfIndex = 0;

    handPerson: Person;
    headPerson: Person;
    personTarget: Person;
    handTarget: Node;
    collidTarget: Node;
    otherplane: PolygonPlane;
    otherBox: Box;
    infusion: Sprite;
    pourCollider: Collider2D;

    width: number;
    height: number;
    handNum = -1;

    sprite: Sprite;

    isMove = false;
    isOverCook = false;
    isTween = false;

    Init(): void {
        super.Init();
        this.sprite = this.getComponent(Sprite);
        this.collider.size.width = this.uiTransform.width;
        this.collider.size.height = this.uiTransform.height;
        this.collider.offset = v2((0.5 - this.uiTransform.anchorX) * this.uiTransform.width, (0.5 - this.uiTransform.anchorY) * this.uiTransform.height);
        this.collider.apply();
        this.schedule(() => {
            if (this.otherBox && this.otherBox.canCook && !this.otherBox.isOpen && !this.isOverCook) {
                let c = color(this.sprite.color);
                c.g > 150 && c.g--;
                c.b > 150 && c.b--;
                c.g == 150 && c.b == 150 && (this.isOverCook = true);
                this.sprite.color = c;
            }
            if (this.isLimitBottle) {
                if (this.water && this.water.fillRange > 0 && this.node.children[0].getComponent(ParticleSystem2D).active) this.water.fillRange -= 0.03;
                else if (this.water && this.water.fillRange < 1 && this.pourCollider) this.water.fillRange += 0.03;
                if (this.water && this.water.fillRange <= 0 && this.node.children[0].getComponent(ParticleSystem2D).active) this.node.children[0].getComponent(ParticleSystem2D).stopSystem();
            }
        }, 0.1);
    }

    HandInit(url: string) {
        this.sprite.spriteFrame = GameData.itemBundle.get(url, SpriteFrame);
        this.rig.type = ERigidBody2DType.Animated;
        this.rig.enabledContactListener = false;
        this.collider.enabled = false;
        this.Init();
    }

    TouchStart(event: EventTouch) {
        if (this.isTween) return;
        if (MigaManager.Instance.tutorial.active) {
            if (GameData.tutorial != 8 || this.node.name != "Item-011") return;
        }
        this.lastPos = this.node.getWorldPosition();
        this.isMove = false;
        this.otherBox = null;
        this.handPerson && this.handPerson.HandOff(this.node);
        if (this.rig.type = ERigidBody2DType.Animated) {
            let temp = PhysicsSystem2D.instance.physicsWorld["_animatedBodies"]
            js.array.remove(temp, this.rig.impl);
        }
        this.rig.type = ERigidBody2DType.Dynamic;
        this.rig.enabledContactListener = true;
        this.collider.enabled = true;
        if (this.canUse) this.node.children[0].active = false;
        if (this.canTouchUse) this.node.children[0].active = true;
        if (this.isBottle) {
            Tween.stopAllByTag(TweenTag.Angle, this.node);
            tween(this.node).tag(TweenTag.Angle)
                .to(0.2, { angle: 120 })
                .call(() => {
                    this.node.children[0].active = true;
                    this.node.children[0].getComponent(ParticleSystem2D)?.resetSystem();
                })
                .start();
        }
        else {
            Tween.stopAllByTag(TweenTag.Angle, this.node);
            tween(this.node)
                .to(0.5, { angle: this.touchAngle }, { easing: EasingType.circOut })
                .start();
        }
        super.TouchStart(event);
    }

    TouchMove(event: EventTouch) {
        if (this.isTween) return;
        console.log(Vec3.equals(this.lastPos, this.node.getWorldPosition()));
        if (!this.isMove && !Vec3.equals(this.lastPos, this.node.getWorldPosition())) this.isMove = true;
        if (MigaManager.Instance.tutorial.active) {
            if (GameData.tutorial != 8 || this.node.name != "Item-011") return;
        }
        super.TouchMove(event);
        if (this.handPerson) {
            for (let i = 0; i < this.handPerson.handContacts.length; i++) {
                const element = this.handPerson.handContacts[i].getComponent(UITransform);
                if (element.getBoundingBoxToWorld().contains(event.getUILocation())) {
                    if (this.handPerson.handTarget[i] || this.handTarget == this.handPerson.handContacts[i]) continue;
                    this.handTarget = this.handPerson.handContacts[i];
                    this.handPerson.HandState(1, i);
                    this.handNum = i;
                }
                else if (this.handTarget == this.handPerson.handContacts[i]) {
                    this.handTarget = null;
                    this.handPerson.HandState(0, i);
                    this.handNum = -1;
                }
            }
        }
    }

    TouchEnd(event: EventTouch) {
        if (this.isTween) return;
        if (!this.isTouch) return;
        this.isMove = false;
        if (MigaManager.Instance.tutorial.active) {
            if (GameData.tutorial != 8 || this.node.name != "Item-011") return;
        }
        MigaManager.Instance.edgePoint = null;
        Tween.stopAllByTag(TweenTag.Touch, this.node);
        tween(this.node).tag(TweenTag.Touch)
            .to(0.1, { scale: Vec3.ONE })
            .start();
        if (this.isBottle) {
            Tween.stopAllByTag(TweenTag.Angle);
            tween(this.node).tag(TweenTag.Angle)
                .to(0.2, { angle: 0 })
                .call(() => {
                    if (!this.node.children[0].getComponent(ParticleSystem2D)) this.node.children[0].active = false;
                    this.node.children[0].getComponent(ParticleSystem2D)?.stopSystem();
                })
                .start();
        }
        this.touchUpPos = this.node.getPosition();
        this.isTouch = false;
        if (this.canTouchUse) this.node.children[0].active = false;
        if (this.isInfusion && this.infusion) {
            let sf = this.infusion.spriteFrame;
            this.infusion.spriteFrame = this.sprite.spriteFrame;
            this.sprite.spriteFrame = sf;
            this.Raycast(event.getUILocation());
        }
        else if (this.handTarget && this.handPerson) {
            this.handPerson.Hand(this);
            Tween.stopAllByTag(TweenTag.Angle, this.node);
            tween(this.node)
                .to(0.5, { angle: this.handAngle }, { easing: EasingType.circOut })
                .call(() => {
                    if (this.isBottle) {
                        if (!this.node.children[0].getComponent(ParticleSystem2D)) this.node.children[0].active = false;
                        this.node.children[0].getComponent(ParticleSystem2D)?.stopSystem();
                    }
                })
                .start();
            if (MigaManager.Instance.tutorial.active) MigaManager.Instance.NextTutorial();
        }
        else if (this.sitTarget) {
            this.node.setParent(this.sitTarget, true);
            let otherWidth = this.sitTarget.getComponent(UITransform).width;
            let delta = this.sitTarget.getWorldPosition().x - this.node.getWorldPosition().x;
            let pos = v3(this.node.getWorldPosition().x, this.sitTarget.getWorldPosition().y);
            if (otherWidth < this.uiTransform.width) pos.x = this.sitTarget.getWorldPosition().x;
            else if (delta > otherWidth / 2 - this.width / 2) pos.x = this.sitTarget.getWorldPosition().x - otherWidth / 2 + this.width / 2;
            else if (delta < this.width / 2 - otherWidth / 2) pos.x = this.sitTarget.getWorldPosition().x + otherWidth / 2 - this.width / 2;
            Tween.stopAllByTag(TweenTag.FallCollid, this.node);
            tween(this.node).tag(TweenTag.FallCollid)
                .to(0.2, { worldPosition: pos })
                .start();
        }
        else {
            this.Raycast(event.getUILocation());
        }
    }

    Raycast(pos: Vec2) {
        this.rig.gravityScale = 8;
        let pos2 = v2();
        Vec2.subtract(pos2, pos, v2(0, 10000));
        const results = PhysicsSystem2D.instance.raycast(pos, pos2, ERaycast2DType.All);
        this.rayTarget = null;
        let result: RaycastResult2D;
        if (results.length > 0) {
            let arr = [...results];
            arr.sort((a, b) => { return Vec2.distance(a.point, pos) - Vec2.distance(b.point, pos) });
            for (let i = 0; i < arr.length; i++) {
                const element = arr[i];
                if (element.collider.group == 1 << 2 || element.collider.group == 1 << 6 && element.collider.node.parent.getComponent(Item).size > this.size || element.collider.group == 1 << 7 && element.collider.node.getComponent(PolygonPlane).size > this.size) {
                    result = element;
                    break;
                }
            }
        }
        if (result) this.rayTarget = result.collider.node;
    }

    ChangeSf() {
        if (this.isMove) return;
        if (this.sfIndex == 1 && !this.canReverse || this.changeSfs.length == 0) return;
        this.sfIndex = this.sfIndex == 0 ? 1 : 0;
        this.sprite.spriteFrame = this.changeSfs[this.sfIndex];
        this.collider.size.width = this.uiTransform.width;
        this.collider.size.height = this.uiTransform.height;
        this.collider.offset = v2((0.5 - this.uiTransform.anchorX) * this.uiTransform.width, (0.5 - this.uiTransform.anchorY) * this.uiTransform.height);
        this.collider.apply();
        if (this.clip) AudioManager.Instance.PlaySFX(this.clip);
    }

    Use() {
        this.node.children[0].active = !this.node.children[0].active;
    }

    ClickAniSpeed() {
        if (this.isMove) return;
        let ani = this.node.children[0].getComponent(Animation);
        ani.getState(ani.defaultClip.name).speed = ani.getState(ani.defaultClip.name).speed == 1 ? 5 : 1;
    }

    Spawn() {
        if (this.isMove) return;
        let node = instantiate(this.spawn);
        node.setParent(MigaManager.Instance.touchPanel);
        let pos = this.node.getWorldPosition();
        pos.y -= 10;
        node.setWorldPosition(pos);
        node.active = true;
    }

    onBeginContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
        if (this.isTween) return;
        let other = otherCollider.node;
        let itemFloor = other.parent.getComponent("ItemFloor") as any;
        if (otherCollider.group == 1 << 14 && this.rig.gravityScale > 0) {
            this.isTween = true;
            this.scheduleOnce(() => { other.parent.getComponent(Basket).Dunk(this); });
            return;
        }
        if (otherCollider.group == 1 << 1) this.personTarget = other.getComponent(Person);
        if (otherCollider.group == 1 << 4 && this.canHand) this.handPerson = other.parent.getComponent(Person);
        if (otherCollider.group == 1 << 11) this.headPerson = other.parent.getComponent(Person);
        if (otherCollider.group == 1 << 12 && this.isInfusion) this.infusion = other.getComponent(Sprite);
        if (otherCollider.group == 1 << 2 || otherCollider.group == 1 << 6 && itemFloor.size > this.size || otherCollider.group == 1 << 7 && other.getComponent(PolygonPlane).size > this.size) {
            if (!this.isTouch && this.rig.gravityScale > 0) {
                if (other.getComponent(PolygonPlane)?.layer) this.otherplane = other.getComponent(PolygonPlane);
                if (other == this.rayTarget) {
                    if (other.getComponent(PolygonPlane)?.floorBox) this.node.setParent(other.getComponent(PolygonPlane).floorBox.node, true);
                    else this.node.setParent(MigaManager.Instance.game.children[0], true);
                    if (other.getComponent(PolygonPlane)?.box) this.otherBox = other.getComponent(PolygonPlane)?.box;
                    this.rig.gravityScale = 0;
                    this.rig.linearVelocity = Vec2.ZERO;
                    let v = v3(0, 0);
                    if (otherCollider.group == 1 << 6 && other.parent.getComponent(Item).size > this.size) {
                        this.scheduleOnce(() => { this.rig.type = ERigidBody2DType.Animated; });
                        this.rig.enabledContactListener = false;
                        this.collider.enabled = false;
                        this.node.setParent(itemFloor.inside, true);
                        let otherUI = other.getComponent(UITransform);
                        let dx = this.node.getWorldPosition().x - other.getWorldPosition().x;
                        let limitX: number;
                        limitX = (otherUI.width - this.uiTransform.width) / 2 * (dx > 0 ? 1 : -1);
                        if (limitX > 0 && dx > limitX - 20 || limitX < 0 && dx < limitX + 20) v.x = -(dx - limitX);
                    }
                    else if (otherCollider.group == 1 << 7 || otherCollider.group == 1 << 2) {
                        let plane = other.getComponent(PolygonPlane);
                        if (plane) {
                            if (plane.box && plane.box.size > this.size) this.node.setParent(plane.box.node, true);
                            let otherUI = other.getComponent(UITransform);
                            let dx = this.node.getWorldPosition().x - other.getWorldPosition().x;
                            let limitX: number;
                            let y = this.node.getWorldPosition().y - this.uiTransform.height * (1 - this.uiTransform.anchorY);
                            if (plane.mul != 0) {
                                let deltaX = otherUI.height * plane.mul;
                                let topY = other.getWorldPosition().y + otherUI.height * (1 - otherUI.anchorY);
                                let per = Math.max((topY - y) / otherUI.height, 0);
                                limitX = (otherUI.width / 2 - deltaX * per) * (dx > 0 ? 1 : -1);
                            }
                            else limitX = (otherUI.width - this.uiTransform.width) / 2 * (dx > 0 ? 1 : -1);
                            if (limitX > 0 && dx > limitX - 20 || limitX < 0 && dx < limitX + 20) v.x = -(dx - limitX);
                        }
                    }
                    let ratio = clamp(Math.abs(this.touchUpPos.y - other.getPosition().y), 0, 2000) / 2000;
                    Tween.stopAllByTag(TweenTag.FallCollid, this.node);
                    tween(this.node).tag(TweenTag.FallCollid)
                        .by(0.2, { position: v3(v.x, 0) }, { easing: EasingType.sineOut })
                        // .by(0.2, { position: v3(0, -110 * ratio) }, { easing: EasingType.sineIn })
                        .start();
                }
                else {
                    if (this.collidTarget && other != this.collidTarget) return;
                    if (other.getComponent(PolygonPlane)?.floorBox) this.node.setParent(other.getComponent(PolygonPlane).floorBox.node, true);
                    else this.node.setParent(MigaManager.Instance.game.children[0], true);
                    if (other.getComponent(PolygonPlane)?.box) this.otherBox = other.getComponent(PolygonPlane)?.box;
                    this.rig.gravityScale = 0;
                    this.rig.linearVelocity = Vec2.ZERO;
                    let v = v3(0, 0);
                    if (otherCollider.group == 1 << 6 && other.parent.getComponent(Item).size > this.size) {
                        this.scheduleOnce(() => { this.rig.type = ERigidBody2DType.Animated; });
                        this.rig.enabledContactListener = false;
                        this.collider.enabled = false;
                        this.node.setParent(itemFloor.inside, true);
                        let otherUI = other.getComponent(UITransform);
                        let dx = this.node.getWorldPosition().x - other.getWorldPosition().x;
                        let limitX: number;
                        limitX = (otherUI.width - this.uiTransform.width) / 2 * (dx > 0 ? 1 : -1);
                        if (limitX > 0 && dx > limitX - 20 || limitX < 0 && dx < limitX + 20) v.x = -(dx - limitX);
                    }
                    else if (otherCollider.group == 1 << 7 || otherCollider.group == 1 << 2) {
                        let plane = other.getComponent(PolygonPlane);
                        if (plane) {
                            if (plane.box && plane.box.size > this.size) this.node.setParent(plane.box.node, true);
                            let otherUI = other.getComponent(UITransform);
                            let dx = this.node.getWorldPosition().x - other.getWorldPosition().x;
                            let limitX: number;
                            let y = this.node.getWorldPosition().y - this.uiTransform.height * (1 - this.uiTransform.anchorY);
                            if (plane.mul != 0) {
                                let deltaX = otherUI.height * plane.mul;
                                let topY = other.getWorldPosition().y + otherUI.height * (1 - otherUI.anchorY);
                                let per = Math.max((topY - y) / otherUI.height, 0);
                                limitX = (otherUI.width / 2 - deltaX * per) * (dx > 0 ? 1 : -1);
                            }
                            else limitX = (otherUI.width - this.uiTransform.width) / 2 * (dx > 0 ? 1 : -1);
                            if (limitX > 0 && dx > limitX - 20 || limitX < 0 && dx < limitX + 20) v.x = -(dx - limitX);
                        }
                    }
                    Tween.stopAllByTag(TweenTag.FallCollid, this.node);
                    let y = this.node.getWorldPosition().y - other.getWorldPosition().y - this.uiTransform.height / 2 * this.uiTransform.anchorY / 0.5;
                    if (y < 0) v.y = -y + 10;
                    tween(this.node).tag(TweenTag.FallCollid)
                        .by(0.2, { position: v })
                        .start();
                }
            }
            else if (!this.collidTarget || this.collidTarget.getWorldPosition().y > other.getWorldPosition().y) this.collidTarget = other;
        }
        else if (otherCollider.group == 1 << 3) {
            if (other.getComponent(PolygonPlane) && other.getComponent(PolygonPlane).size <= this.size) return;
            if (this.isTouch) this.sitTarget = other;
            else if (this.rig.gravityScale > 0) {
                this.rig.gravityScale = 0;
                this.rig.linearVelocity = Vec2.ZERO;
                this.node.setParent(other, true);
                let otherWidth = other.getComponent(UITransform).width;
                let delta = other.getWorldPosition().x - this.node.getWorldPosition().x;
                let pos = v3(this.node.getWorldPosition().x, other.getWorldPosition().y);
                if (otherWidth < this.uiTransform.width) pos.x = other.getWorldPosition().x;
                else if (delta > otherWidth / 2 - this.width / 2) pos.x = other.getWorldPosition().x - otherWidth / 2 + this.width / 2;
                else if (delta < this.width / 2 - otherWidth / 2) pos.x = other.getWorldPosition().x + otherWidth / 2 - this.width / 2;
                Tween.stopAllByTag(TweenTag.FallCollid, this.node);
                tween(this.node).tag(TweenTag.FallCollid)
                    .to(0.2, { worldPosition: pos })
                    .start();
            }
        }
        else if (otherCollider.group == 1 << 18) {
            if (this.rig.gravityScale > 0) {
                this.isTween = true;
                Tween.stopAllByTarget(this.node);
                tween(this.node)
                    .to(0.5, { worldPosition: other.getWorldPosition(), scale: Vec3.ZERO })
                    .call(() => { this.node.destroy() })
                    .start();
            }
        }
        else if (otherCollider.group == 1 << 19) {
            if (this.rig.gravityScale == 0 && !this.isTouch) {
                if (this.pourCollider != otherCollider) this.pourCollider = otherCollider;
            }
        }
    }

    onEndContact(selfCollider: Collider2D, otherCollider: Collider2D, contact: IPhysics2DContact | null) {
        let other = otherCollider.node;
        if (otherCollider.group == 1 << 1 && this.personTarget == other.getComponent(Person) && this.rig.type == ERigidBody2DType.Dynamic) this.personTarget = null;
        if (otherCollider.group == 1 << 4 && this.handPerson == other.parent.getComponent(Person) && this.handPerson.handContacts.indexOf(other) == this.handNum && this.rig.type == ERigidBody2DType.Dynamic) this.handPerson = null;
        if (otherCollider.group == 1 << 11 && this.headPerson == other.parent.getComponent(Person) && this.rig.type == ERigidBody2DType.Dynamic) this.headPerson = null;
        else if (otherCollider.group == 1 << 3 && this.isTouch && other == this.sitTarget) this.sitTarget = null;
        else if (other == this.collidTarget) this.collidTarget = null;
        if (otherCollider.group == 1 << 12 && this.infusion == other.getComponent(Sprite)) this.infusion = null;
        if (otherCollider.group == 1 << 19) {
            if (this.pourCollider == otherCollider) this.pourCollider = null;
        }
    }

}
